﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using umbraco.blog4.interfaces;
using umbraco.cms.businesslogic;
using umbraco.BasePages;

namespace umbraco.blog4 {
    public class Comment : ICommentsContainer
    {
        #region .ctor
        public Comment(int commentId) : this(new Document(commentId)) {}

        public Comment(Document node)
        {
            // TODO: Decide what to do with invalid nodes - RJF
            if (node == null)
                return;

            if (BlogLibrary.TypeAliasInherits(DocumentTypeName, node.ContentType.Alias))
            {
                BaseDocument = node;

                // Get the parent of the Comment, validating to make sure it is a Comments Container.
                if (BaseDocument.Parent != null)
                {
                    var postId = BlogLibrary.FindParentId(Post.DocumentTypeName, node.Id);
                    if (postId > -1)
                        Post = new Post(postId);

                    if (BlogLibrary.IsType(DocumentTypeName, BaseDocument.ParentId))
                        Container = new Comment(BaseDocument.ParentId);
                    else
                        Container = Post;
                }

                /// TODO: Decide whether to throw an exception if the comment is an orphan... - RJF
            }
            else
            {
                throw new InvalidOperationException("The given node ID does not point to a valid Comment.");
            }
        }

        /// <summary>
        /// Creates a new comment populating it with the supplied parameters
        /// </summary>
        /// <param name="postId"></param>
        /// <param name="name"></param>
        /// <param name="email"></param>
        /// <param name="website"></param>
        /// <param name="text"></param>
        public Comment(int postId, string name, string email, string website, string text) : 
            this(new Document(postId), name, email, website, text) { }

        /// <summary>
        /// Creates a new comment populating it with the supplied parameters
        /// </summary>
        /// <param name="postId"></param>
        /// <param name="name"></param>
        /// <param name="email"></param>
        /// <param name="website"></param>
        /// <param name="text"></param>
        public Comment(Document container, string name, string email, string website, string text)
        {

            if (BlogLibrary.TypeAliasInherits(Post.DocumentTypeName, container.ContentType.Alias))
                Post = new Post(container);
            else
            {
                var postId = BlogLibrary.FindParentId(Post.DocumentTypeName, container.Id);
                if (postId > -1)
                    Post = new Post(postId);
            }

            if (BlogLibrary.TypeAliasInherits(DocumentTypeName, container.ContentType.Alias))
            {
                Container = new Comment(container);
                CreateCommentInternal(name, email, website, text, container);
            }
            else {
                Container = Post;
                if (Post != null) {
                    foreach (Document d in Post.BaseDocument.Children)
                    {
                        if (BlogLibrary.TypeAliasInherits("Comments", d.ContentType.Alias))
                        {
                            CreateCommentInternal(name, email, website, text, d);
                            break;
                        }
                    }
                }
            }
            if (Container == null)
                throw new InvalidOperationException("Received Document cannot contain Comments.");
        }

        private void CreateCommentInternal(string name, string email, string website, string text, Document container)
        {
            BaseDocument = Document.MakeNew(name, Blog.DTBlogComment, User.GetCurrent(), container.Id);
            Email = email;
            Name = name;
            Website = website;
            Text = text;
        }
        #endregion

        #region Properties
        public int Id { get { return BaseDocument.Id; } }
        public string Name {
            get { return BaseDocument.getProperty("name").Value.ToString(); }
            set { BaseDocument.getProperty("name").Value = value; BaseDocument.Save(); }
        }
        public string Email {
            get { return BaseDocument.getProperty("email").Value.ToString(); }
            set { BaseDocument.getProperty("email").Value = value; BaseDocument.Save(); }
        }
        public string Website {
            get { return BaseDocument.getProperty("website").Value.ToString(); }
            set { BaseDocument.getProperty("website").Value = value; BaseDocument.Save(); }
        }
        public string GravatarURL {
            get { return String.Format("http://www.gravatar.com/avatar/{0}.jpg", umbraco.library.md5(Email)); }
        }
        public string Text {
            get { return BaseDocument.getProperty("bodyText").Value.ToString(); }
            set { BaseDocument.getProperty("bodyText").Value = value; BaseDocument.Save(); }
        }
        public bool IsSpam {
            get { return BaseDocument.getProperty("isSpam").Value.ToString() == "1"; }
            set { 
                BaseDocument.getProperty("isSpam").Value = value ? 1 : 0;
                BaseDocument.Save();
                if (value)
                    UnPublish();
            }
        }

        public bool IsPublished {
            get { return BaseDocument.Published; }
        }

        public DateTime Created
        {
            get
            {
                return BaseDocument.CreateDateTime;
            }
        }
        public Document BaseDocument { get; private set; }
        
        public ICommentsContainer Container { get; set; }
        public Post Post { get; private set; }
        public void Publish()
        {
            BaseDocument.Publish(User.GetCurrent() ?? User.GetUser(0));
        }
        public void UnPublish()
        {
            BaseDocument.UnPublish();
        }
        #endregion

        #region ICommentsContainer Implementation
        public Blog Blog { get { return Post.Blog; } }
        public static string DocumentTypeName { get { return "Comment"; } }

        public IEnumerable<Comment> Comments
        {
            get
            {
                return (from d in BaseDocument.Children
                        // - Do we perhaps just want to check for Blog.DTBlogComment instead? - RJF
                        where BlogLibrary.TypeAliasInherits(d.ContentType.Alias, d.ContentType.Alias)
                        select new Comment(d));
            }
        }

        /// <summary>
        /// Gets the Maximum Depth of nested comments and trackbacks.
        /// </summary>
        /// <remarks>
        /// This method primarily exists to assist in using XSLT recursion to nest the comments as far as needed.
        /// </remarks>
        public int CommentDepth
        {
            get
            {
                int depth = 0;
                foreach (var d in Comments)
                {
                    if (d.CommentDepth > depth)
                        depth = d.CommentDepth;
                }

                return depth + 1;
            }
        }
        #endregion
    }
}